package com.spring.nio.service.file.handler;

import com.spring.nio.core.channel.context.ChannelContext;
import com.spring.nio.core.channel.context.SocketChannelContext;
import com.spring.nio.core.channel.handler.AbstractChannelHandler;
import com.spring.nio.core.event.listener.event.constant.ExceptionEventTypeEnum;
import com.spring.nio.core.event.listener.event.model.ExceptionEventSource;
import com.spring.nio.core.event.model.PacketData;
import com.spring.nio.core.exception.ExceptionManager;
import com.spring.nio.service.file.frame.FileMessageFrame;
import com.spring.nio.service.file.util.FileFrameSpecificParse;
import com.spring.nio.util.BasicUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.CollectionUtils;
import java.io.IOException;
import java.io.UnsupportedEncodingException;

/**
 * 文件帧字节解析处理器
 * @author spring
 * */

@Slf4j
@SuppressWarnings("all")
public class FileFrameDecodeHandler extends AbstractChannelHandler {

    @Override
    public void handler(Object o, ChannelContext channelContext) throws IOException {
        PacketData packetData = (PacketData) o;
        if(CollectionUtils.isEmpty(packetData.getCompleteList())) {
            return;
        }

        // 解析数据
        packetData.getCompleteList().stream().forEach(groupData -> {
            try {
                // groupData.getBytes()字节数组已经是完整一帧的数据(只需执行帧的处理即可)
                this.parseDataBytes(groupData.getBytes(), packetData.getSocketChannelContext(), channelContext);
            } catch (Exception e) {
                // 异常事件(ExceptionEvent) 事件源类型为异常事件源类型(EXCEPTION_EVENT_SOURCE)  事件源数据(NioReadEventExceptionSource)
                ExceptionEventSource exceptionEventSource = new ExceptionEventSource();
                exceptionEventSource.setSocketChannelContext(packetData.getSocketChannelContext());
                exceptionEventSource.setModule("FileFrameDecodeHandler --> method [handler] --> 产生解析帧异常");
                exceptionEventSource.setException(e);
                exceptionEventSource.setExceptionEventTypeEnum(ExceptionEventTypeEnum.FILE_PARSE_HANDLER_IO_EXCEPTION);
                exceptionEventSource.setIsSend(Boolean.TRUE);
                ExceptionManager.handleException(exceptionEventSource);
            }
        });
    }

    /**
     * 解码字节数据(能解析多少完整帧就解析多少)
     * @param sumBytes  原始数据
     * @param socketChannelContext
     * @param channelContext
     * */
    private void parseDataBytes(byte[] sumBytes, SocketChannelContext socketChannelContext, ChannelContext channelContext) {
        /**
         *  每次进行新的帧解析需要判断剩余字节数据是否够一帧的基本数据,不够3个或18个字节，则不进行处理，等待下次数据到来一并处理,
         *    此处直接跳出解析循环
         * */
        if((sumBytes.length < FileMessageFrame.UPLOAD_COMMON) || (sumBytes.length < FileMessageFrame.ONLINE_TRANSPORT_COMMON)) {
            return;
        }

        // 是否完成帧中基本数据的解析
        boolean isCompleteFrameBasicParse = true;
        Integer i = 0;
        FileMessageFrame fileMessageFrame = null;
        while (i < sumBytes.length) {
            if(isCompleteFrameBasicParse) {
                // 2、创建帧对象
                fileMessageFrame = new FileMessageFrame();
                // 3、解析完成基本的文件帧后，
                i = this.decodeFileFrameHandler(i, sumBytes, fileMessageFrame, channelContext, socketChannelContext);
                // 4、设置帧基本数据解析完成标志 (即[是否结束帧] [帧序号] [帧类型] [文件类型] [文件操作类型]解析完成)
                isCompleteFrameBasicParse = false;
            } else {
                // 4、校验帧基本数据是否解析完整
                if(!this.checkFileMessageFrame(i, fileMessageFrame)) {
                    if(fileMessageFrame.getFrameType().equals(FileMessageFrame.FrameType.ONLINE_TRANSPORT)) {
                        // 校验失败，未解析成功在线传输帧基本数据，则跳过，尝试继续解析下一帧
                        i = i + FileMessageFrame.ONLINE_TRANSPORT_COMMON;
                    }

                    if(fileMessageFrame.getFrameType().equals(FileMessageFrame.FrameType.UPLOAD)) {
                        i = i + FileMessageFrame.UPLOAD_COMMON;
                    }
                    continue;
                }

                // 5、读取文件名称长度数据并设置下一个索引位置，即需要开始解析新的帧
                i = this.parseData(i, sumBytes, fileMessageFrame);
                socketChannelContext.getRealList().add(fileMessageFrame);
                isCompleteFrameBasicParse = true;
            }
        }
    }

    /**
     * 解析文件帧基本数据
     *
     * @param i 从第几个字节开始处理
     * @param sumBytes 总共字节数
     * @param fileMessageFrame 当前数据帧
     * @param channelContext 当前handler所属处理器链上下文
     * @param socketChannelContext 当前通道SocketChannel附件
     * @return index 返回文件基本数据解析后的字节索引，即读取真实数据开始的索引位置
     */
    private int decodeFileFrameHandler(Integer i, byte[] sumBytes, FileMessageFrame fileMessageFrame, ChannelContext channelContext, SocketChannelContext socketChannelContext) {
        FileFrameSpecificParse parse = FileFrameSpecificParse.getParse();

        // 1、是否是结束帧 1B
        fileMessageFrame.setEndFrame(sumBytes[i]);

        // 2、帧序号 4B
        byte[] indexBytes = new byte[4];
        indexBytes[0] = sumBytes[i + 1];
        indexBytes[1] = sumBytes[i + 2];
        indexBytes[2] = sumBytes[i + 3];
        indexBytes[3] = sumBytes[i + 4];
        fileMessageFrame.setFrameIndex(BasicUtil.byteArrayToInt(indexBytes));

        // 3、解析帧类型、文件类型、文件操作类型 3B (三个字节)
        parse.executeBasicFileFrameParse(sumBytes[i + 5], sumBytes[i + 6], sumBytes[i + 7], fileMessageFrame);

        // 4、此处需要根据帧类型做不同处理
        FileMessageFrame.FrameType frameType = fileMessageFrame.getFrameType();
        // 4.1、在线传输帧或上传帧或下载帧
        if(frameType.equals(FileMessageFrame.FrameType.ONLINE_TRANSPORT)
            || frameType.equals(FileMessageFrame.FrameType.UPLOAD)
            || frameType.equals(FileMessageFrame.FrameType.DOWNLOAD)) {

            return parse.executeDynamicFileFrameParse(i, sumBytes, fileMessageFrame);
        }

        // 4.2、文件实时字节流数据传输帧 --> 即发送文件流实时数据  --> 此时的handler直接跳转至处理文件流转发handler
        if(frameType.equals(FileMessageFrame.FrameType.DATA_TRANSPORT)) {
            parse.executeUploadStreamDataTransport(i, sumBytes, fileMessageFrame, channelContext);
            // 返回文件流字节数据末尾，不再进行后续帧数据解析
            return i = sumBytes.length;
        }

        // 4.3、实时文件数据传输结束帧(关闭发起端和接收端两个用户关联的FileSocektChannel)
        if(frameType.equals(FileMessageFrame.FrameType.DATA_TRANSPORT_END)) {
            // 6.1、 解析实时文件流数据帧
            parse.executeDataTransportEndFrameParse(i, sumBytes, fileMessageFrame, socketChannelContext);
            return i = sumBytes.length;
        }

        // 4.4、在线传输实时流处理
        if(frameType.equals(FileMessageFrame.FrameType.SEND_DATA_TRANSPORT)) {
            parse.executeOnlineTransportStreamDataTransport(i, sumBytes, fileMessageFrame, channelContext);
            socketChannelContext.getRealList().add(fileMessageFrame);
            return i = sumBytes.length;
        }

        // 4.5、在线传输实时流处理结束
        if(frameType.equals(FileMessageFrame.FrameType.SEND_DATA_TRANSPORT_END)) {
            parse.executeDataTransportEndFrameParse(i, sumBytes, fileMessageFrame, socketChannelContext);
            // 返回文件流字节数据末尾，不再进行后续帧数据解析
            return i = sumBytes.length;
        }

        return 0;
    }

    /**
     * 校验帧是否完整
     * @param i 从第几个字节开始处理
     * @param fileMessageFrame
     * @para return
     */
    private Boolean checkFileMessageFrame(Integer i, FileMessageFrame fileMessageFrame) {
        // 在线传输帧校验
        if(fileMessageFrame.getFrameType().equals(FileMessageFrame.FrameType.ONLINE_TRANSPORT)) {
            if(/*null == fileMessageFrame.getFrameSumLength()
                    ||*/ null == fileMessageFrame.getFrameType()
                    || null == fileMessageFrame.getFileOperateType()
                    || null == fileMessageFrame.getFileType()) {
                return Boolean.FALSE;
            }
        }

        // 上传帧
        if(fileMessageFrame.getFrameType().equals(FileMessageFrame.FrameType.UPLOAD)) {
            if(/*null == fileMessageFrame.getFrameSumLength()
                    ||*/ null == fileMessageFrame.getFrameType()
                    || null == fileMessageFrame.getFileOperateType()
                    || null == fileMessageFrame.getFileType()
                    || null == fileMessageFrame.getDataLength()
                    || null == fileMessageFrame.getFileLength()) {
                return Boolean.FALSE;
            }

            if(/*0 == fileMessageFrame.getFrameSumLength()
                    ||*/ 0 == fileMessageFrame.getDataLength()
                    || 0 == fileMessageFrame.getFileLength()) {
                return Boolean.FALSE;
            }
        }

        return Boolean.TRUE;
    }

    /**
     * 读取数据
     * @param i
     * @param sumBytes
     * @param fileMessageFrame
     * @return index 返回文件真实数据解析后的字节索引
     */
    private int parseData(Integer i, byte[] sumBytes, FileMessageFrame fileMessageFrame) {
        byte[] content = new byte[fileMessageFrame.getDataLength()];

        int k = 0;
        for(int j = i; j < (i + content.length); j++) {
            content[k] = sumBytes[j];
            k++;
        }

        // 5、解析原始数据
        try {
            fileMessageFrame.setData(new String(content, "UTF-8"));
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }

        return i + content.length;
    }
}
